<template>
	<view
		v-if="!visible"
		class="console-toggle"
		:style="{ left: btnPos.x + 'rpx', top: btnPos.y + 'rpx' }"
		@touchstart="onTouchStart"
		@touchmove="onTouchMove"
		@touchend="onTouchEnd"
		@mousedown="onTouchStart"
		@mousemove="onTouchMove"
		@mouseup="onTouchEnd"
		@click="toggle"
	>
		<v-button>控制台</v-button>
	</view>
	<view v-if="visible" class="v-console" :class="{ expanded: expanded }">
		<view class="toolbar">
			<view class="buttons">
				<v-text-button size="medium" @click="toggle">关闭</v-text-button>
				<v-text-button size="medium" @click="toggleExpand">
					{{ expanded ? '收起' : '展开' }}
				</v-text-button>
				<v-text-button size="medium" @click="clear">清空</v-text-button>
			</view>
			<view class="filter">
				<v-input v-model="keyword" placeholder="关键字筛选" class="keyword" @confirm="onKeywordConfirm" />
				<v-select-picker :options="levels" :value="levelIndex" @confirm="onLevelConfirm" class="level" />
			</view>
		</view>
		<scroll-view scroll-y class="list">
			<view v-for="(log, i) in showList" :key="i" :class="'log ' + log.type">
				<text v-if="log.time" class="time">{{ log.time }} {{ log.type }}</text>
				<v-text :class="['msg', log.type]" :lines="log.blockLines" @longpress="copyLog(log.msg)">{{ log.msg }}</v-text>
				<v-text-button class="detail-info" @click="log.blockLines == 2 ? (log.blockLines = 999) : (log.blockLines = 2)">
					{{ log.blockLines == 2 ? '详细' : '简略' }}
				</v-text-button>
			</view>
		</scroll-view>
	</view>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';

const STORAGE_KEY = 'v-console-logs';
const visible = ref(false);
const toggle = () => (visible.value = !visible.value);

const expanded = ref(false);
const toggleExpand = () => (expanded.value = !expanded.value);

const logs = ref([]);
const keyword = ref('');
const levelIndex = ref(0);
const levels = [
	{ label: '全部', value: 0 },
	{ label: 'log', value: 1 },
	{ label: 'info', value: 2 },
	{ label: 'warn', value: 3 },
	{ label: 'error', value: 4 },
	{ label: 'debug', value: 5 }
];

const btnPos = ref({ x: 30, y: 100 });
const dragging = ref(false);
const startPos = ref({ x: 0, y: 0 });

const rpx2px = (rpx) => (rpx * uni.getSystemInfoSync().windowWidth) / 750;

onMounted(() => {
	loadLogs();
});

const getPos = (e) => {
	// #ifdef H5
	return { x: e.clientX, y: e.clientY };
	// #endif
	// #ifndef H5
	const touch = e.touches[0] || e.changedTouches[0];
	return { x: touch.clientX, y: touch.clientY };
	// #endif
};

/* 按下 */
const onTouchStart = (e) => {
	dragging.value = true;
	startPos.value = getPos(e);
};

/* 移动 */
const onTouchMove = (e) => {
	if (!dragging.value) return;
	const { windowWidth, windowHeight } = uni.getSystemInfoSync();
	const cur = getPos(e);
	const dx = cur.x - startPos.value.x;
	const dy = cur.y - startPos.value.y;

	/* 转成 rpx */
	const newX = btnPos.value.x + (dx * 750) / windowWidth;
	const newY = btnPos.value.y + (dy * 750) / windowHeight;

	/* 边界限制：0~屏幕宽/高 - 按钮宽/高(≈120rpx) */
	btnPos.value.x = Math.max(0, Math.min(newX, (windowWidth * 750) / windowWidth - 120));
	btnPos.value.y = Math.max(0, Math.min(newY, (windowHeight * 750) / windowWidth - 120));

	startPos.value = cur;
};

const onTouchEnd = () => (dragging.value = false);

if (typeof window !== 'undefined') {
	window.onerror = (msg, url, line, col, error) => {
		console.error('[Global Error]', msg, `${url}:${line}:${col}`, error?.stack);
	};
	window.onunhandledrejection = (e) => {
		console.error('[Unhandled Promise]', e.reason);
	};
} else {
	// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-QQ
	wx.onError?.((res) => console.error('[wx Error]', res));
	wx.onUnhandledRejection?.((res) => console.error('[wx Promise]', res.reason));
	// #endif
}

['log', 'info', 'warn', 'error', 'debug'].forEach((type) => {
	const origin = console[type];
	console[type] = (...args) => {
		origin.apply(console, args);
		const time = new Date().toLocaleTimeString();
		const msg = args.map((v) => (typeof v === 'object' ? JSON.stringify(v) : String(v))).join(' ');
		const newLog = { type, msg, time, blockLines: 2 };
		logs.value.unshift(newLog);
		saveLogs();
	};
});

const showList = computed(() => {
	let list = logs.value;
	if (levelIndex.value) {
		list = list.filter((l) => l.type === levels[levelIndex.value].label.toLowerCase());
	}

	if (keyword.value.trim()) {
		const k = keyword.value.trim().toLowerCase();
		list = list.filter((l) => l.msg.toLowerCase().includes(k));
	}
	list.blockLines = 2;
	return list;
});
const saveLogs = () => {
	try {
		// #ifdef H5
		sessionStorage.setItem(STORAGE_KEY, JSON.stringify(logs.value));
		// #endif
		// #ifndef H5
		uni.setStorageSync(STORAGE_KEY, logs.value);
		// #endif
	} catch (_) {}
};

const loadLogs = () => {
	try {
		let data = null;
		// #ifdef H5
		data = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '[]');
		// #endif
		// #ifndef H5
		data = uni.getStorageSync(STORAGE_KEY) || [];
		// #endif
		logs.value = data;
	} catch (_) {}
};

const clear = () => {
	logs.value = [];
	try {
		// #ifdef H5
		sessionStorage.removeItem(STORAGE_KEY);
		// #endif
		// #ifndef H5
		uni.removeStorageSync(STORAGE_KEY);
		// #endif
	} catch (_) {}
};

const copyLog = (msg) => {
	// #ifdef H5
	const input = document.createElement('textarea');
	input.value = msg;
	document.body.appendChild(input);
	input.select();
	document.execCommand('copy');
	document.body.removeChild(input);
	uni.showToast({ title: '已复制', icon: 'none' });
	// #endif
	// #ifndef H5
	uni.setClipboardData({ data: msg, showToast: false });
	uni.showToast({ title: '已复制', icon: 'none' });
	// #endif
};
const onKeywordConfirm = (e) => {
	console.log(e);
	keyword.value = e;
};
const onLevelConfirm = (e) => {
	levelIndex.value = e;
};
</script>

<style lang="scss" scoped>
/* 悬浮开关按钮（黑底白字） */
.console-toggle {
	position: fixed;
	right: 30rpx;
	bottom: 100rpx;
	z-index: 10000;
}

/* 控制台主体：白底黑字 */
.v-console {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 9999;
	background: #fff;
	color: #000;
	font-family: monospace;
	font-size: 0.75rem;
	height: 120rpx; /* 默认收缩高度 */
	transition: height 0.25s ease;
	border-top: 1rpx solid #e5e5e5;

	&.expanded {
		height: 50%; /* 展开后高度 */
	}

	.toolbar {
		display: flex;
		padding: 12rpx 24rpx;
		background: #f7f7f7;
		border-bottom: 1rpx solid #e5e5e5;
		flex-direction: column;
		.buttons {
			display: flex;
		}
		.filter {
			display: flex;
			.keyword {
				flex: 1;
				margin-right: 20rpx;
			}
			.level {
				width: 200rpx;
				:deep(.select-input) {
					height: 60rpx;
					line-height: 60rpx;
				}
			}
		}
	}

	.list {
		height: calc(100% - 100rpx);
		padding: 16rpx;
		.log {
			display: flex;
			line-height: 1.4;
			margin-bottom: 8rpx;
			.time {
				margin-right: 12rpx;
				color: #666;
			}
			.msg {
				width: 50%;
				&.log {
					color: #008000;
				}
				&.info {
					color: #0066cc;
				}
				&.warn {
					color: #ff6600;
				}
				&.error {
					color: #cc0000;
				}
				&.debug {
					color: #666;
				}
			}
		}
	}
}
</style>
